/*
 * Copyright (c) 2011, okazoh_tk. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  - Neither the name of the copyright holder nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include "alkes/opengl/GLProgram.h"
#include "alkes/opengl/GLShader.h"

namespace alkes {

GLProgram* GLProgram::create(Shader* vertex_shader, Shader* fragment_shader)
{
    GLShader* gl_vertex_shader = restrict_cast<GLShader*>(vertex_shader);
    GLShader* gl_fragment_shader = restrict_cast<GLShader*>(fragment_shader);

    AL_PRE_COND(
        gl_vertex_shader && AL_SHADER_TYPE_VERTEX == gl_vertex_shader->getShaderType(),
        0);

    AL_PRE_COND(
        gl_fragment_shader && AL_SHADER_TYPE_FRAGMENT == gl_fragment_shader->getShaderType(),
        0);

    GLuint program;
    AL_OGL_INVOKE(program = glCreateProgram());
    if (program == 0)
    {
        LOGE("Failed to create program.");
        return 0;
    }

    AL_OGL_INVOKE(glAttachShader(program, gl_vertex_shader->getShader()));
    AL_OGL_INVOKE(glAttachShader(program, gl_fragment_shader->getShader()));

    glLinkProgram(program);
    GLint link_result;
    glGetProgramiv(program, GL_LINK_STATUS, &link_result);
    if (GL_FALSE == link_result)
    {
        GLint info_len;
        glGetProgramiv(program, GL_INFO_LOG_LENGTH, &info_len);
        if (info_len > 0)
        {
            TExtendBuffer<> temp;
            GLchar* p_temp = reinterpret_cast<GLchar*>(temp.getBuffer(info_len));
            glGetProgramInfoLog(program, info_len, 0, p_temp);
            LOGE("Program link error: %s", p_temp);
        }

        glDeleteProgram(program);
        return 0;
    }

    return new GLProgram(program, vertex_shader, fragment_shader);
}

void GLProgram::destroy()
{
    delete this;
}

GLProgram::GLProgram(GLuint program, Shader* vertex_shader, Shader* fragment_shader)
: Program(vertex_shader, fragment_shader)
, program_(program)
, matrix_(glGetUniformLocation(program, "u_mvpMatrix"))
{
}

GLProgram::~GLProgram()
{
    glDeleteProgram(program_);
}

bool GLProgram::useProgram()
{
    AL_OGL_INVOKE(glUseProgram(program_));
    return true;
}

GLuint GLProgram::getProgram() const
{
    return program_;
}

GLint GLProgram::getMatrixLocation() const
{
    return matrix_;
}

}
